package com.ruoyi.common.utils.file;

import com.jcraft.jsch.*;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

/**
 * @author Administrator
 */
@Component
public class SftpUtil {
    private static final Logger log = LoggerFactory.getLogger(SftpUtil.class);

    /**
     * ip
     */
    private String ftpIp;
    /**
     *  端口
     */
    private Integer PORT;

    /**
     * 用户名
     */
    private String USERNAME;

    /**
     * 密码
     */
    private String PASSWORD;


    /**
     * 路径
     */
    private String PATH = "";

    private Session session = null;
    private Channel channel = null;

    public SftpUtil() {

    }

    public SftpUtil(String ip, String username, String password) {
        this.ftpIp = ip;
        this.USERNAME = username;
        this.PASSWORD = password;
    }

    public SftpUtil(String ip, int port, String username, String password) {
        this.ftpIp = ip;
        this.PORT = port;
        this.USERNAME = username;
        this.PASSWORD = password;
    }

    /**
     *
     * 连接ftp服务器
     * @throws IOException
     *
     **/
    public boolean connectServer()  {
        JSch jsch = new JSch();
        try {
            log.debug("需要登陆ip: {}", ftpIp);
            this.session = jsch.getSession(this.USERNAME, this.ftpIp, this.PORT);
            this.session.setPassword(this.PASSWORD);
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            // 为Session对象设置properties
            this.session.setConfig(config);
            // 设置timeout时间
            this.session.setTimeout(Integer.MAX_VALUE);
            // 通过Session建立链接
            this.session.connect();
            // 打开SFTP通道
            this.channel = this.session.openChannel("sftp");
            // 建立SFTP通道的连接
            this.channel.connect();
            // 用2进制上传、下载
            log.info("登陆ip: {} 成功",this.ftpIp);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 关闭sftp连接
     *
     * @throws Exception
     */
    public void closeServer() {
        try {
            if (channel != null) {
                channel.disconnect();
            }
            if (session != null) {
                session.disconnect();
            }
        } catch (Exception e) {
            log.info("关闭链接失败");
            e.printStackTrace();
        }

    }

    /**
     * 上传文件
     *
     * @param in          要上传的文件
     * @param newDir      新的文件存放路径
     * @param newFileName 新的文件名
     */
    public void put(InputStream in, String newDir, String newFileName) throws Exception {
        if (this.channel == null || !this.channel.isConnected()) {
            this.connectServer();
        }
        ChannelSftp chsftp = (ChannelSftp) channel;
        try {
            log.debug("文件上传地址：文件目录： {}  ,文件名称： {}, 传输流是否为空：{}" , newDir ,newFileName,in==null);
            createPath(newDir, chsftp);
            chsftp.put(in, newFileName, ChannelSftp.APPEND);
            log.info("上传文件成功，文件名称 ：{}",newFileName);
        } catch (Exception e) {
            log.info("上传文件失败，文件名称 ：{}",newFileName);
            throw e;
        }finally {
            this.closeServer();
        }

    }

    /**
     * 目录创建
     *
     * @param newDir
     * @param chsftp
     * @throws SftpException
     */
    private void createPath(String newDir, ChannelSftp chsftp) {
        log.info("开始创建目录： {}",newDir);
        if (channel == null || !channel.isConnected()) {
            this.connectServer();
        }
        if (chsftp == null) {
            chsftp = (ChannelSftp) channel;
        }

        try {
            log.info("目录已存在： {}",newDir);
            if (fileExist(newDir)) {
                chsftp.cd(newDir);
                return;
            }
            String[] pathArray = newDir.split("/");
            StringBuffer filePath = new StringBuffer("/");
            for (String path : pathArray) {
                if (path.equals("")) {
                    continue;
                }
                filePath.append(path + "/");
                if (fileExist(filePath.toString())) {
                    chsftp.cd(filePath.toString());
                } else {
                    // 建立目录
                    chsftp.mkdir(filePath.toString());
                    // 进入并设置为当前目录
                    chsftp.cd(filePath.toString());
                }
            }
            log.info("创建目录成功： {}",newDir);
            chsftp.cd(newDir);
        } catch (SftpException e) {
            log.error("创建路径错误：" + newDir);
            e.printStackTrace();
        }
    }

    /**
     * 上传文件
     *
     * @param newDir      新的文件存放路径
     * @param newFileName 新的文件名（全名）
     */
    public void put(BufferedImage img, String newDir, String newFileName) throws Exception {
        ChannelSftp chsftp = (ChannelSftp) channel;
        try {
            createPath(newDir, chsftp);
            InputStream in = null;
            ImageOutputStream ios = null;
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ios = ImageIO.createImageOutputStream(os);
            ImageIO.write(img, "jpg", ios);
            in = new ByteArrayInputStream(os.toByteArray());
            chsftp.put(in, newDir + newFileName);
            chsftp.quit();
            this.closeServer();
            System.out.println("图片上传成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("图片上传出错！");
        }finally {
            this.closeServer();
        }

    }

    /**
     * 文件合并
     *
     * @param targetFile
     * @param folder
     */
    public String merge(String targetFile, String folder, String filename) throws Exception {
        String returnStr ="200";

            if (channel == null || !channel.isConnected()) {
                this.connectServer();
            }
        try {
            ChannelSftp channelSftp = (ChannelSftp) channel;
            if (this.fileExist(targetFile)) {
                returnStr = "300";
            }

            createPath(folder, channelSftp);
            log.debug("folder:{}",folder);
            channelSftp.cd(folder);
            List<String> list = new ArrayList<>();
            Vector<?> vector = channelSftp.ls(folder);
            for (Object item : vector) {
                LsEntry entry = (LsEntry) item;
                String name = entry.getFilename();
                log.info("获取文件名称： {}",name);
                if (name.indexOf("-") > 0) {
                    list.add(name);
                }

            }

            //合并文件
            list.stream().filter(a -> !a.equals(filename))
                    .sorted((o1, o2) -> {
                        String p1 = o1;
                        String p2 = o2;
                        int i1 = p1.lastIndexOf("-");
                        int i2 = p2.lastIndexOf("-");
                        return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
                    })
                    .forEach(b -> {
                        InputStream in = null;
                        try {
                        	
                            in = channelSftp.get(b);
                            byte[] fileData = IOUtils.toByteArray(in);
                            InputStream sbs = new ByteArrayInputStream(fileData);
                            channelSftp.put(sbs, targetFile, ChannelSftp.APPEND);
                            channelSftp.rm(b);
                        } catch (SftpException | IOException e) {
                            e.printStackTrace();
                            log.error(e.getMessage(), e);
                        }
                        log.info("合并文件名称：{}",b);
                    });
            log.debug("文件合并地址：" + targetFile);
        } catch (Exception e) {
            returnStr = "400";
            log.error(e.getMessage(), e);
        }finally {
            this.closeServer();
        }
        return returnStr;
    }


    /**
     * 下载文件
     *
     * @param directory    下载目录
     * @param downloadFile 下载的文件
     */
    public InputStream download(String directory, String downloadFile) throws Exception {
        try {
            if (channel == null || !channel.isConnected()) {
                this.connectServer();
            }
            ChannelSftp channelSftp = (ChannelSftp) channel;
            channelSftp.cd(directory);
            InputStream in = channelSftp.get(downloadFile);
            byte[] fileData = IOUtils.toByteArray(in);
            System.out.println("数据长度：" + fileData.length);
            InputStream sbs = new ByteArrayInputStream(fileData);
            return sbs;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            this.closeServer();
        }
    }

    /**
     * 删除文件
     *
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     */
    public void delete(String directory, String deleteFile) {
        try {
            if (channel == null || !channel.isConnected()) {
                this.connectServer();
            }
            ChannelSftp sftp = (ChannelSftp) channel;
            sftp.cd(directory);
            sftp.rm(deleteFile);
            log.info("文件夹：{} 下的文件：{} ,删除成功",directory,deleteFile);
        } catch (Exception e) {
            log.info("文件夹：{} 下的文件：{} ,删除失败",directory,deleteFile);
            e.printStackTrace();
        }finally {
            this.closeServer();
        }
    }

    /**
     * 移动文件
     *
     * @param oldPath
     * @param newPath
     * @param newDir
     */
    public void rename(String oldPath, String newPath, String newDir) {
        try {
            if (channel == null || !channel.isConnected()) {
                this.connectServer();
            }
            ChannelSftp sftp = (ChannelSftp) this.channel;
            createPath(newDir, sftp);
            sftp.rename(oldPath, newPath);
            this.closeServer();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            this.closeServer();
        }

    }


    public boolean fileExist(String directory) {
        if (this.channel == null || !this.channel.isConnected()) {
            this.connectServer();
        }
        boolean dirExist = false;
        try {
            ChannelSftp sftp = (ChannelSftp) this.channel;
            SftpATTRS sftpATTRS = sftp.lstat(directory);
            log.info("文件已存在,文件名称：{}",directory);
            return true;
        } catch (Exception e) {
            log.error(e.getMessage());
            if ("no such file".equalsIgnoreCase(e.getMessage())) {
                dirExist = false;
            }
            log.info("文件不存在,文件名称：{}",directory);
        }
        return dirExist;
    }

}
